package com.xj.shop.manage.all.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xj.shop.enums.Admin;
import com.xj.shop.enums.Shop;
import com.xj.shop.manage.all.mapper.OrderMapper;
import com.xj.shop.manage.all.model.dto.OrderDTO;
import com.xj.shop.manage.all.model.dto.OrderLogDTO;
import com.xj.shop.manage.all.model.dto.OrderPriceCalculationDTO;
import com.xj.shop.manage.all.model.dto.OrderUpdDTO;
import com.xj.shop.manage.all.model.entity.*;
import com.xj.shop.manage.all.model.query.OrderQuery;
import com.xj.shop.manage.all.model.vo.*;
import com.xj.shop.manage.all.service.*;
import io.github.wslxm.springbootplus2.common.auth.entity.JwtUser;
import io.github.wslxm.springbootplus2.common.auth.util.JwtUtil;
import io.github.wslxm.springbootplus2.core.base.service.impl.BaseServiceImpl;
import io.github.wslxm.springbootplus2.core.config.threadpool.XjThreadUtil;
import io.github.wslxm.springbootplus2.core.utils.BeanDtoVoUtil;
import io.github.wslxm.springbootplus2.core.utils.BigDecimalUtil;
import io.github.wslxm.springbootplus2.core.utils.validated.ValidUtil;
import io.github.wslxm.springbootplus2.manage.sys.model.dto.MsgDTO;
import io.github.wslxm.springbootplus2.manage.sys.model.entity.SysUser;
import io.github.wslxm.springbootplus2.manage.sys.service.MsgService;
import io.github.wslxm.springbootplus2.manage.sys.service.SysUserService;
import io.github.wslxm.springbootplus2.starter.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 订单表 ServiceImpl
 *
 * <p>
 * ::本代码由[兮家小二]提供的代码生成器生成,如有问题,请手动修改 ::作者CSDN:https://blog.csdn.net/qq_41463655
 * </p>
 * @author ws
 * @email 1720696548@qq.com
 * @date 2022-08-24 18:56:38
 */
@Service
public class OrderServiceImpl extends BaseServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private AddressService addressService;

    @Autowired
    private GoodsSpecsService goodsSpecsService;

    @Autowired
    private GoodsService goodsService;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private OrderLogService orderLogService;

    @Autowired
    private MsgService msgService;

    @Autowired
    private SysUserService sysUserService;

    @Override
    public IPage<OrderVO> findPage(OrderQuery query) {
        Page<OrderVO> page = new Page<OrderVO>(query.getCurrent(), query.getSize());
        List<OrderVO> list = baseMapper.list(page, query);
        page.setRecords(list);
        return page;
    }

    @Override
    public OrderVO findId(String id) {
        Order order = this.getById(id);
        OrderVO orderVO = BeanDtoVoUtil.convert(order, OrderVO.class);
        User user = userService.getById(order.getUserId());
        orderVO.setFullName(user.getFullName());
        orderVO.setNickname(user.getNickname());
        orderVO.setPhone(user.getPhone());
        return orderVO;
    }


    /**
     * 方法加锁, 防止库存超卖
     * @param dto dto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized String createOrder(OrderDTO dto) {
        JwtUser jwtUser = JwtUtil.getJwtUser(request);
        // 判断收货地址
        Address address = addressService.getById(dto.getAddressId());
        ValidUtil.isTrue(address == null, "没有获取到收货地址信息");

        // 验证规格
        List<String> goodsSpecsIds = dto.getSpecses().stream().map(OrderDTO.Specs::getGoodsSpecsId).collect(Collectors.toList());
        List<GoodsSpecs> goodsSpecsList = goodsSpecsService.listByIds(goodsSpecsIds);
        ValidUtil.isTrue(goodsSpecsList == null || goodsSpecsList.isEmpty(), "没有获取到商品规格数据");
        Map<String, GoodsSpecs> goodsSpecsMap = goodsSpecsList.stream().collect(Collectors.toMap(GoodsSpecs::getId, p -> p));

        // 验证商品
        List<String> goodsIds = goodsSpecsList.stream().map(GoodsSpecs::getGoodsId).collect(Collectors.toList());
        List<Goods> goodsList = goodsService.list(new LambdaQueryWrapper<Goods>()
                .select(Goods.class, info -> !"details".equals(info.getColumn()))
                .in(Goods::getId, goodsIds));
        ValidUtil.isTrue(goodsList == null, "没有获取到商品数据");
        Map<String, Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods::getId, p -> p));

        for (Goods goods : goodsList) {
            ValidUtil.isTrue(goods.getState().equals(Shop.GoodsState.V0.getValue()), goods.getName() + " 商品已下架");
        }

        // 获取交易号，如果立即发起支付多订单（购物车）共用一个交易号，如果后续到订单列表支付，请生成新的交易号替换
        String payNo = redisUtil.getOrderNo("xj-pay");
        List<OrderDTO.Specs> specses = dto.getSpecses();
        List<Order> orders = new ArrayList<>();
        for (OrderDTO.Specs specs : specses) {
            String orderNo = redisUtil.getOrderNo("xj-order");
            GoodsSpecs goodsSpecs = goodsSpecsMap.get(specs.getGoodsSpecsId());
            ValidUtil.isTrue(goodsSpecs == null, "规格数据不存在");

            Goods goods = goodsMap.get(goodsSpecs.getGoodsId());
            ValidUtil.isTrue(goods == null, goodsSpecs.getSpecsName() + " 规格的商品数据不存在或已删除");

            GoodsVO goodsVO = BeanDtoVoUtil.convert(goods, GoodsVO.class);
            goodsVO.setGoodsSpecsList(CollUtil.newArrayList(BeanDtoVoUtil.convert(goodsSpecs, GoodsSpecsVO.class)));
            AddressVO addressVO = BeanDtoVoUtil.convert(address, AddressVO.class);

            // 验库存 扣库存
            ValidUtil.isTrue(goodsSpecs.getSpecsStock() - specs.getNum() < 0, goods.getName() + goodsSpecs.getSpecsName() + "该此规格的库存不足");
            GoodsSpecs updGoodsSpecs = new GoodsSpecs();
            updGoodsSpecs.setId(goodsSpecs.getGoodsId());
            updGoodsSpecs.setSpecsStock(goodsSpecs.getSpecsStock() - specs.getNum());
            boolean b1 = goodsSpecsService.updateById(updGoodsSpecs);

            // 创建订单
            Order order = new Order();
            order.setUserId(jwtUser.getUserId());
            order.setUnitPrice(goodsSpecs.getSpecsPrice());
            order.setNum(specs.getNum());
            order.setTotalPrice(BigDecimalUtil.multiply(goodsSpecs.getSpecsPrice(), new BigDecimal(specs.getNum() + "")));
            order.setOrderNo(orderNo);
            order.setGoodsId(goods.getId());
            order.setPayNo(payNo);
            order.setGoodsSpecsId(goodsSpecs.getId());
            order.setAddressId(dto.getAddressId());
            order.setGoodsDetails(JSON.toJSONString(goodsVO));
            order.setAddressDetails(JSON.toJSONString(addressVO));
            order.setState(Shop.OrderState.V1.getValue());
            order.setUserRemark(dto.getUserRemark());
            orders.add(order);
        }
        boolean b = this.saveBatch(orders);

        // 异步记录订单日志
        XjThreadUtil.asyncExecute(() -> {
            List<OrderLogDTO> dtos = new ArrayList<>();
            for (Order order : orders) {
                OrderLogDTO orderLogDTO = new OrderLogDTO();
                orderLogDTO.setOrderId(order.getId());
                orderLogDTO.setOperationType(Shop.OperationType.V1);
                orderLogDTO.setFrontState(null);
                orderLogDTO.setAfterState(Shop.OrderState.V1.getValue());
                orderLogDTO.setRemarks("用户下单");
                dtos.add(orderLogDTO);

                // 给 系统管理员 人员发送消息
                List<SysUser> sysUsers = sysUserService.list(new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getPosition, Admin.Position.V0.getValue()));
                GoodsVO goodsVO = JSON.parseObject(order.getGoodsDetails(), GoodsVO.class);
                for (SysUser sysUser : sysUsers) {
                    MsgDTO msgDTO = new MsgDTO();
                    msgDTO.setUserId(sysUser.getId());
                    msgDTO.setContent("用户[" + jwtUser.getFullName() + "]创建了商品[" + goodsVO.getName() + "]的订单");
                    msgDTO.setUserType(Admin.MsgUserType.V1.getValue());
                    msgDTO.setMsgType(Admin.MsgType.V3.getValue());
                    // 路由参数
                    Map<String, String> routeParams = new HashMap<>();
                    routeParams.put("orderNo", order.getOrderNo());
                    msgDTO.setRouteParams(routeParams);
                    msgDTO.setIsWebsocket(true);
                    msgService.insert(msgDTO);
                }
            }
            orderLogService.insertBatch(dtos);
        });


        // 返回交易号
        return payNo;
    }


    @Override
    public boolean upd(String id, OrderUpdDTO dto) {
        Order entity = dto.convert(Order.class);
        entity.setId(id);
        return this.updateById(entity);
    }

    @Override
    public boolean deliverGoods(String id, String logisticsNo) {
        Order order = this.getById(id);

        Order entity = new Order();
        entity.setId(id);
        entity.setLogisticsNo(logisticsNo);
        entity.setState(Shop.OrderState.V3.getValue());
        entity.setDeliverTime(LocalDateTime.now());
        this.updateById(entity);

        // 异步记录订单日志
        XjThreadUtil.asyncExecute(() -> {
            OrderLogDTO orderLogDTO = new OrderLogDTO();
            orderLogDTO.setOrderId(order.getId());
            orderLogDTO.setOperationType(Shop.OperationType.V4);
            orderLogDTO.setFrontState(order.getState());
            orderLogDTO.setAfterState(Shop.OrderState.V3.getValue());
            orderLogDTO.setRemarks("订单发货");
            orderLogService.insert(orderLogDTO);
        });

        return true;
    }

    @Override
    public boolean updTotalPrice(String id, BigDecimal totalPrice) {
        Order order = this.getById(id);

        Order entity = new Order();
        entity.setId(id);
        entity.setTotalPrice(totalPrice);
        this.updateById(entity);

        // 异步记录订单日志
        XjThreadUtil.asyncExecute(() -> {
            List<OrderLogDTO> dtos = new ArrayList<>();
            OrderLogDTO orderLogDTO = new OrderLogDTO();
            orderLogDTO.setOrderId(order.getId());
            orderLogDTO.setOperationType(Shop.OperationType.V9);
            orderLogDTO.setFrontState(order.getState());
            orderLogDTO.setAfterState(order.getState());
            orderLogDTO.setRemarks("订单改价, 修改前：" + order.getTotalPrice() + " 修改后：" + totalPrice);
            dtos.add(orderLogDTO);
            orderLogService.insertBatch(dtos);
        });
        return true;
    }

    @Override
    public boolean updState(String id, Integer state) {
        Order order = this.getById(id);

        Order entity = new Order();
        entity.setId(id);
        entity.setState(state);
        // 录入完成时间
        if (state.equals(Shop.OrderState.V4.getValue())) {
            entity.setSuccessTime(LocalDateTime.now());
        }
        // 取消订单刷掉交易号
        if (state.equals(Shop.OrderState.V5.getValue())) {
            entity.setPayNo("");
        }
        this.updateById(entity);

        // 异步记录订单日志
        XjThreadUtil.asyncExecute(() -> {
            Shop.OperationType operationType = null;
            String remarks = "未知";
            if (state.equals(Shop.OrderState.V5.getValue())) {
                operationType = Shop.OperationType.V3;
                remarks = "用户取消订单";
            } else if (state.equals(Shop.OrderState.V4.getValue())) {
                operationType = Shop.OperationType.V6;
                remarks = "用户收货";
            }
            OrderLogDTO orderLogDTO = new OrderLogDTO();
            orderLogDTO.setOrderId(order.getId());
            orderLogDTO.setOperationType(operationType);
            orderLogDTO.setFrontState(order.getState());
            orderLogDTO.setAfterState(state);
            orderLogDTO.setRemarks(remarks);
            orderLogService.insert(orderLogDTO);
        });
        return true;
    }

    @Override
    public boolean pay(String payNo) {
        List<Order> orders = this.list(new LambdaQueryWrapper<Order>().eq(Order::getPayNo, payNo));
        // 这里实际开发应为发起支付,通过支付的回调修改状态值
        // 这里默认支付成功
        this.update(new Order(), new LambdaUpdateWrapper<Order>()
                .set(Order::getState, Shop.OrderState.V2.getValue())
                .set(Order::getPayTime, LocalDateTime.now())
                .in(Order::getPayNo, payNo));

        // 异步记录订单日志
        XjThreadUtil.asyncExecute(() -> {
            List<OrderLogDTO> dtos = new ArrayList<>();
            for (Order order : orders) {
                OrderLogDTO orderLogDTO = new OrderLogDTO();
                orderLogDTO.setOrderId(order.getId());
                orderLogDTO.setOperationType(Shop.OperationType.V2);
                orderLogDTO.setFrontState(order.getState());
                orderLogDTO.setAfterState(Shop.OrderState.V2.getValue());
                orderLogDTO.setRemarks("订单支付成功");
                dtos.add(orderLogDTO);
            }
            orderLogService.insertBatch(dtos);
        });

        return true;
    }

    @Override
    public boolean del(String id) {
        return this.removeById(id);
    }

    @Override
    public OrderPriceCalculationVO priceCalculation(List<OrderPriceCalculationDTO> dtos) {
        OrderPriceCalculationVO vo = new OrderPriceCalculationVO();
        if (dtos == null || dtos.size() == 0) {
            return vo;
        }
        // 获取规格
        List<String> goodsSpecsIds = dtos.stream().map(OrderPriceCalculationDTO::getGoodsSpecsId).collect(Collectors.toList());
        List<GoodsSpecs> goodsSpecsList = goodsSpecsService.listByIds(goodsSpecsIds);
        ValidUtil.isTrue(goodsSpecsList.size() != dtos.size(), "规格不存在");
        Map<String, GoodsSpecs> goodsSpecsMap = goodsSpecsList.stream().collect(Collectors.toMap(GoodsSpecs::getId, p -> p));

        // 获取商品
        Set<String> goodsIds = goodsSpecsList.stream().map(GoodsSpecs::getGoodsId).collect(Collectors.toSet());
        List<Goods> goodsList = goodsService.listByIds(goodsIds);
        Map<String, Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods::getId, p -> p));

        // 计算每个商品规格的价格 及 总价数据
        BigDecimal totalPrice = new BigDecimal("0");
        List<OrderPriceCalculationVO.SpecsPriceVO> specsPriceVos = new ArrayList<>();
        for (OrderPriceCalculationDTO dto : dtos) {
            GoodsSpecs goodsSpecs = goodsSpecsMap.get(dto.getGoodsSpecsId());
            Goods goods = goodsMap.get(goodsSpecs.getGoodsId());
            //
            BigDecimal specsTotalPrice = BigDecimalUtil.multiply(goodsSpecs.getSpecsPrice(), new BigDecimal(dto.getNum() + ""));
            OrderPriceCalculationVO.SpecsPriceVO specsPriceVO = new OrderPriceCalculationVO.SpecsPriceVO();
            specsPriceVO.setGoodsSpecsId(dto.getGoodsSpecsId());
            specsPriceVO.setSpecsPrice(goodsSpecs.getSpecsPrice());
            specsPriceVO.setTotalPrice(specsTotalPrice);
            specsPriceVO.setNum(dto.getNum());
            specsPriceVO.setName(goods.getName());
            specsPriceVO.setCoverPic(goods.getCoverPic());
            specsPriceVO.setSpecsName(goodsSpecs.getSpecsName());
            specsPriceVos.add(specsPriceVO);
            totalPrice = BigDecimalUtil.add(totalPrice, specsTotalPrice);
        }
        vo.setSpecsPriceVos(specsPriceVos);
        vo.setTotalPrice(totalPrice);
        return vo;
    }
}
